package org.colin.interceptor;

import java.lang.reflect.Method;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.colin.Tools.RedisUtil;
import org.colin.Tools.TokenUtil;
import org.colin.Tools.Tool;
import org.colin.constant.Constants;
import org.colin.exceptions.NoLoginException;
import org.colin.model.vo.UserVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

/**
 * @author wujiangbo
 * @desc 验证token的拦截器
 * @date 2020年1月3日 下午3:04:36
 */
public class AuthenticationInterceptor implements HandlerInterceptor {

	@Autowired
	private RedisUtil redisUtil;

	@Autowired
	private TokenUtil tokenUtil;

	@Value("${system.tokenTimeout}")
	private Integer tokenTimeout;

	@Value("${system.tokenFlag}")
	private String tokenFlag;

	@Override
	public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
		if (Tool.isBlank(tokenFlag) || "1".equals(tokenFlag)) {
			// 如果不是映射到方法直接通过
			if (!(object instanceof HandlerMethod)) {
				return true;
			}
			HandlerMethod handlerMethod = (HandlerMethod) object;
			Method method = handlerMethod.getMethod();
			String methodName = method.getName();
			String whiteList = "getPictureVerification,login,uiConfiguration,swaggerResources,getDocumentation,securityConfiguration";
			if (whiteList.contains(methodName)) {
				// 请求白名单方法，就放行，其余方法需要验证token
				return true;
			}
			// 获取当前用户请求头信息中的token信息
			String headToken = this.tokenUtil.getToken();
			// 获取登录者信息
			UserVO user = tokenUtil.getRedisUser();
			if (Tool.isBlank(headToken)) {
				throw new NoLoginException("请求头信息中无token，请重新登录");
			}
			String tokenKey = Constants.TOKEN_KEY_STRING + headToken;
			// 获取当前用户在Redis中的token信息，然后与传来的对比
			String redisToken = (String) redisUtil.get(tokenKey);
			String userIdToken = (String) redisUtil.get(user.getId());
			if (Tool.isBlank(redisToken) || Tool.isBlank(userIdToken)) {
				throw new NoLoginException("token失效，请重新登录");
			}
			if (!headToken.equals(redisToken)) {
				throw new NoLoginException("token错误");
			}
			if (!headToken.equals(userIdToken)) {
				throw new NoLoginException("请重新登录");
			}
			// 重置token失效时间
			redisUtil.expire(tokenKey, tokenTimeout, "3");
			redisUtil.expire(user.getId(), tokenTimeout, "3");

			String userInfoRedisKey = Constants.REDIS_USER_INFO_KEY + headToken;
			Map<Object, Object> map = redisUtil.hmget(userInfoRedisKey);
			if (map == null) {
				throw new NoLoginException("无用户信息，请重新登录");
			}
			// Redis中会话的用户信息对象
			UserVO userInfo = (UserVO) map.get(Constants.MAP_USER_INFO_KEY);
			if (userInfo == null) {
				throw new NoLoginException("无用户信息，请重新登录");
			}
			// 重置用户信息失效时间
			redisUtil.expire(userInfoRedisKey, tokenTimeout, "3");
			// 检验用户是否在线（根据userId在redis查不到token就表示被强制下线了）
			String onLineToken = (String) redisUtil.get(userInfo.getId());
			if (Tool.isBlank(onLineToken)) {
				throw new NoLoginException("请重新登录");
			}
			return true;
		} else {
			return true;
		}
	}

	@Override
	public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
	}

	@Override
	public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
	}
}
